import type { SourceType, TargetType, XYCoord } from '../classes/interface';

export type FactoryOrInstance<T> = T | (() => T);
export interface DropTargetHookSpec<DragItem, DropResult, CollectedProps> {
  accept: TargetType;
  dropNode: HTMLElement | null;

  drop?: (
    item: DragItem,
    monitor: DropTargetMonitor<DragItem, DropResult>,
  ) => DropResult | undefined;

  hover?: (
    item: DragItem,
    monitor: DropTargetMonitor<DragItem, DropResult>,
  ) => void;

  canDrop?: (
    item: DragItem,
    monitor: DropTargetMonitor<DragItem, DropResult>,
  ) => boolean | boolean;

  collect?: (
    monitor: DropTargetMonitor<DragItem, DropResult>,
  ) => CollectedProps;
}

export interface DropTargetMonitor<DragItem = unknown, DropResult = unknown> {
  receiveHandlerId: (handlerId: string) => void;
  subscribeStateChange: (listenerFn: () => void) => void;
  getItem: () => DragItem;
  updateItem: (newItem: DragItem) => void;
  getDragType: () => SourceType;
  getDragStartNodeClientOffset: () => XYCoord | undefined;
  getDragStartEventClientOffset: () => XYCoord | undefined;
  getClientOffset: () => XYCoord | undefined;
  getDropNodeClientOffset: () => XYCoord | undefined;
  canDrop: () => boolean;
  isOver: () => boolean;
}

export interface DropTarget {
  setSpec: (newSpec: any) => void;
  getAccept: () => TargetType;
  canDrop: () => boolean;
  hover: () => void;
  drop: () => any;
  getDropNode: () => HTMLElement | null;
}
